package genetics;

import java.util.ArrayList;
import java.util.HashMap;

import games.eightPuzzle.heuristics.AStarMD;
import games.eightPuzzle.heuristics.Heuristic;
import games.eightPuzzle.heuristics.ImprovedMDHuristic;
import games.eightPuzzle.states.SlidingPuzzleState;


//TODO add to the interface the method get weight and sets an arrays
//who will sum the wights. compar not by size, because not always size matters
//thus, compare by weight. (in may game witght = 1 because size matters)
public class DescendantCreator<E extends GeneticInterface> {

	
	private HashMap<String, Integer> _fTrack;
	private HashMap<String, Integer> _mTrack;
	
	private ArrayList<E> _father;
	private ArrayList<Integer> _fWeight;
	
	private ArrayList <E> _mother;
	private ArrayList<Integer> _mWeight;
	
	
	private int _depth;
	public DescendantCreator(){
		
		_fTrack = new HashMap<String, Integer>();
		_mTrack = new HashMap<String, Integer>();
		_fWeight = new ArrayList<Integer>();
		_mWeight = new ArrayList<Integer>();
		
		
	}
	
	private void loadTreck(ArrayList<E> states,ArrayList<Integer> weight, HashMap<String,Integer> treck){
		treck.clear();
		weight.clear();
//		SlidingPuzzleState lastState=null;
		int i=0;
		int w=0;
		for(E e:states){
			treck.put(e.hash(), i++);
			w+=e.weight();
			weight.add(w);
		}
		/*
		//ignore the last state since its the final state and its common to all states
		if (state!=null){
			state = state.getFather();
		}
		while (state!=null){
			treck.put(state.getHash(),state);
			lastState = state;
			state = state.getFather();
		}
		
		//remove the first state since its common to all states
		if (lastState != null){
			treck.remove(lastState.getHash());
		}
		*/
	}
	private ArrayList<E> mergeSolutions(ArrayList<E> left, int leftIndex, 
										ArrayList<E> right, int rightIndex){
		ArrayList<E> merged = new ArrayList<E>();
		for(int i=0;i<leftIndex;i++){
			merged.add(left.get(i));
		}
		for(int i=rightIndex;i<right.size();i++){
			merged.add(right.get(i));
		}
		return merged;
	}
	
	private ArrayList<E> findBestDescendant(){
		int bestFIndex=-1;
		int bestMIndex=-1;
		boolean fatherIsLeft = true;
		//for every common states from the solution, try to build new solution
		//base from the mother's head + father's tail or father's head + mother's tail,
		//the shortest.
		String commonState = "";
		for(String fHash:_fTrack.keySet()){
			Integer mIndex = _mTrack.get(fHash) ;
			if (mIndex!=null){
				
				Integer fIndex = _fTrack.get(fHash);
				
				if (fIndex==0&&mIndex==0||
					fIndex==_father.size()-1&&mIndex==_mother.size()-1){
					break;
				}
				
				if (_fWeight.get(fIndex) + (_mWeight.get(_mother.size()-1)-_mWeight.get(mIndex)) < _depth){
					commonState = fHash;
					bestFIndex = fIndex;
					bestMIndex = mIndex;
					_depth = _fWeight.get(fIndex) + (_mWeight.get(_mother.size()-1)-_mWeight.get(mIndex));
					fatherIsLeft = true;
				}
				
				if (_mWeight.get(mIndex) + (_fWeight.get(_father.size()-1)-_fWeight.get(fIndex)) < _depth){
					commonState = fHash;
					bestFIndex = fIndex;
					bestMIndex = mIndex;
					_depth = _mWeight.get(mIndex) + (_fWeight.get(_father.size()-1)-_fWeight.get(fIndex));
					fatherIsLeft = false;
				}
			}
		}
		
		//no mutual state has been found or the descendant is longer from
		//the shortest parent, return the shortest parent.
		if (bestFIndex==-1 || bestMIndex==-1){
			return _fWeight.get(_father.size()-1)>_mWeight.get(_mother.size()-1)?_mother:_father;
		}
		System.out.println("intersection: "+commonState);
		if (fatherIsLeft){
			return mergeSolutions(_father, bestFIndex, _mother, bestMIndex);
		}else{
			return mergeSolutions(_mother, bestMIndex, _father, bestFIndex);
		}		
	}
	
	public ArrayList<E> generateDescendant(
			ArrayList<E> father,
			ArrayList<E> mother){
		
		_father = father;
		_mother = mother;
		
		loadTreck(_father,_fWeight,_fTrack);
		loadTreck(_mother,_mWeight,_mTrack);
		
		_depth = Math.min(_fWeight.get(_father.size()-1), _mWeight.get(_mother.size()-1));
		
		return findBestDescendant();
	}
	
	public static void main(String[] args){
		ArrayList<SlidingPuzzleState> bank = new ArrayList<SlidingPuzzleState>();
		
		SlidingPuzzleState finalState = new SlidingPuzzleState("C:\\HUJI\\Devel\\AI\\ex1\\fState.txt");
		Heuristic h = new ImprovedMDHuristic(finalState,1);
		Heuristic totalFunction = new AStarMD();
		
		SlidingPuzzleState common1 = new SlidingPuzzleState("C:\\HUJI\\Devel\\AI\\ex1\\common.txt");
		common1.setHeuristics(h, totalFunction);
		
		SlidingPuzzleState common2 = new SlidingPuzzleState("C:\\HUJI\\Devel\\AI\\ex1\\common.txt");
		common2.setHeuristics(h, totalFunction);
		
		
		SlidingPuzzleState s1 = new SlidingPuzzleState("C:\\HUJI\\Devel\\AI\\ex1\\state1.txt");
		SlidingPuzzleState s2 = new SlidingPuzzleState("C:\\HUJI\\Devel\\AI\\ex1\\state2.txt");
		s1.setHeuristics(h, totalFunction);
		s2.setHeuristics(h, totalFunction);
		
		ArrayList<SlidingPuzzleState> father = new ArrayList<SlidingPuzzleState>();
		ArrayList<SlidingPuzzleState> mother = new ArrayList<SlidingPuzzleState>();
		
		for(int i=0;i<5;i++){
			father.add(s1);
		}
		father.add(common1);
		for(int i=0;i<50;i++){
			father.add(s1);
		}
		
		for(int i=0;i<50;i++){
			mother.add(s2);
		}
		mother.add(common2);
		for(int i=0;i<5;i++){
			mother.add(s2);
		}
		
		System.out.println("father len: " + father.size());
		System.out.println("mother len: " + mother.size());
		
		DescendantCreator<SlidingPuzzleState> d = new DescendantCreator<SlidingPuzzleState>();
		System.out.println("son len: " + d.generateDescendant(father, mother).size());
		
		
		/*for(int i=0;i<3;i++){
			SlidingPuzzleState s = new SlidingPuzzleState("C:\\HUJI\\Devel\\AI\\ex1\\state1.txt");
			s.setFather(s1);
			s.setDepth(s1.getDepth()+1);
			s1 = s;
		}
		
		common1.setDepth(s1.getDepth()+1);
		common1.setFather(s1);
		s1 = common1;
		
		for(int i=0;i<10;i++){
			SlidingPuzzleState s = new SlidingPuzzleState("C:\\HUJI\\Devel\\AI\\ex1\\state1.txt");
			s.setFather(s1);
			s.setDepth(s1.getDepth()+1);
			s1 = s;
		}
		
		for(int i=0;i<4;i++){
			SlidingPuzzleState s = new SlidingPuzzleState("C:\\HUJI\\Devel\\AI\\ex1\\state2.txt");
			s.setFather(s2);
			s.setDepth(s2.getDepth()+1);
			s2 = s;
		}
		
		common2.setDepth(s2.getDepth()+1);
		common2.setFather(s2);
		s2 = common2;
		
		for(int i=0;i<2;i++){
			SlidingPuzzleState s = new SlidingPuzzleState("C:\\HUJI\\Devel\\AI\\ex1\\state2.txt");
			s.setFather(s2);
			s.setDepth(s2.getDepth()+1);
			s2 = s;
		}*/
		
		/*System.out.println("Sol1 length: "+s1.getDepth());
		System.out.println("Sol2 length: "+s2.getDepth());
		
		DescendantCreator d = new DescendantCreator();
		System.out.println(d.generateDescendant(s1, s2).getDepth());*/
	}
	
}
